home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
DEMON
/
UTILS
/
EXTARCT.ARC
/
c
/
ExTARct
Wrap
Text File
|
1995-08-20
|
19KB
|
619 lines
/* ExTARct.c
* Tar file extractor.
* (C) 1995 Kevin F. Quinn
* Email: kevq@banana.demon.co.uk
*
* Rationale - to provide an automated facility for extracting tar archives
* in a RiscOS-friendly way, i.e. reversing name/type, and inserting an extra
* directory level to cope with the (grr!) 77-entry directory limit.
*
* Syntax:
* ExTARct [options] <tarfile>
* where options are as follows:
* -v The ubiquitous verbose option
* -d <number> The number of files to one directory (default 60)
* -t <filename> Filename listing types to swap, tagged for directory split
* (default is no swaps, no directory splits)
* -l List operations (i.e. file mappings) without doing anything really
* -p <prefix> Prefix for directory splits (default none)
* -c Enable filename crunching (uses fileswitch truncation by default)
* Unimplemented as yet.
*
*/
/*
* Version 0.1
* Crunching not supported yet.
* Gets very slow when it is searching split subdirectories.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tar.h"
#include "kernel.h"
#include "swis.h"
#define FALSE (0==1)
#define TRUE (1==1)
#define MAXTYPES 256
#define MAXLEN 256
#define MAXENTS 77
#define BUFSIZE 78*256
static int dsize;
static int verbose;
static int list;
static int crunch;
static char *types[MAXTYPES];
static int splits[MAXTYPES];
static int tc;
static char *prefix;
static char buf[BUFSIZE];
int makedir(char *filename) {
char directory[MAXLEN];
char *cptr;
_kernel_oserror *kerr=NULL;
_kernel_swi_regs rin, rout;
if (*filename=='\0') return(FALSE);
cptr=filename+strlen(filename);
while (cptr>filename && *cptr!='.') cptr--;
if (*cptr=='.') {
strcpy(directory,filename);
directory[cptr-filename]='\0';
rin.r[0]=8;
rin.r[1]=(int)directory;
rin.r[2]=0; /* default directory size */
kerr=_kernel_swi(OS_File,&rin,&rout);
if (kerr==NULL) {
return(TRUE);
} else {
if (makedir(directory)) {
return(makedir(filename));
} else {
return(FALSE);
}
}
} else {
return(TRUE); /* No directory; no need to create */
}
}
/* Convert octal string to number */
static int otoi(char *str) {
int num=0;
while (*str==' ') str++;
while (*str>='0' && *str<'8') {
num=num*8+((*str)-'0');
str++;
}
return(num);
}
static void roify(char *str) {
/* Convert the following:
*
* / -> .
* . -> /
* :,*,#,$,&,@,^,%,\,|,",SPACE -> ~<n> (where n=0..9,A,B)
* ~ -> ~~
*
* Note that this conversion is reversible.
* Note also that no check is performed that MAXLEN is not exceeded,
* which in theory is possible. However, tar names are 100 chars max,
* so the problem should not arise.
*/
char tstr[MAXLEN];
int i=0;
strcpy(tstr,str);
while (tstr[i]!='\0') {
switch (tstr[i]) {
/* Swap directory and filetype separators*/
case '/': *str='.'; break;
case '.': *str='/'; break;
/* Escape the nasties */
case ':': *str++='~'; *str='0'; break;
case '*': *str++='~'; *str='1'; break;
case '#': *str++='~'; *str='2'; break;
case '$': *str++='~'; *str='3'; break;
case '&': *str++='~'; *str='4'; break;
case '@': *str++='~'; *str='5'; break;
case '^': *str++='~'; *str='6'; break;
case '%': *str++='~'; *str='7'; break;
case '\\': *str++='~'; *str='8'; break;
case '|': *str++='~'; *str='9'; break;
case '"': *str++='~'; *str='A'; break;
case ' ': *str++='~'; *str='B'; break;
case '~': *str++='~'; *str='~'; break;
default:
/* character is OK */;
}
i++;
str++;
}
}
static int translatename(const char *tarname, char *roname) {
/* Right; this is the cunning bit.
*
* Name translation.
* split to Directory,filename, converting /->., .->/ in directory name
* split filename to name.type (search back from end to first '.')
* if name is empty, return FALSE
* if no type
* roname=directory.name
* else
* if type is in types list
* if splits[n] (& in types list)
* find first subdirectory in directory 1,2,3... which has less than dsize entries
* roname=directory.sub.type.name
* else
* roname=directory.type.name
* else
* roname=directory.name/type
* return TRUE;
*/
char tname[MAXLEN];
char *direct;
char *name;
char *type;
char *tptr;
int tn, fc;
int found;
int subnum;
char scratch[MAXLEN];
_kernel_oserror *kerr=NULL;
_kernel_swi_regs rin, rout;
int tmpind;
strcpy(tname,tarname);
tptr=tname+strlen(tname);
while (tptr>tname && *tptr!='/') tptr--;
if (*tptr!='/') {
direct=NULL;
name=tname;
} else {
direct=tname;
*tptr='\0';
name=tptr+1;
}
if (*name!='\0') { /* no name => just a directory */
tptr=name+strlen(name);
while (tptr>name && *tptr!='.') tptr--;
if (*tptr!='.') {
type=NULL;
} else {
*tptr='\0';
type=tptr+1;
}
} else {
name=NULL;
type=NULL;
}/* if not just a directory */
/* Sort out any RO special character problems */
if (direct!=NULL) roify(direct);
if (name!=NULL) roify(name);
if (type!=NULL) roify(type);
if (name!=NULL) {/* if not just a directory */
if (type==NULL) {
/* roname=directory.name*/
if (direct!=NULL) {
strcpy(roname,direct);
tptr=roname+strlen(direct);
*tptr++='.';
*tptr='\0';
} else {
tptr=roname;
}
strcpy(tptr,name);
} else {
/* if type is in types list*/
tn=0;
/* The 'while' condition below avoids evaluating types[tn] when no types are valid (i.e. tn=tc=0) */
while ((tn<tc)?(strcmp(types[tn],type)!=0):FALSE) tn++;
if (tn<tc) {
/*
* find first subdirectory in directory 1,2,3... which has less than dsize entries
* roname=directory.sub.type.name
*/
if (direct!=NULL) {
strcpy(scratch,direct);
tptr=scratch+strlen(direct);
*tptr++='.';
*tptr='\0';
} else {
tptr=scratch;
*tptr='\0';
}
if (splits[tn]) {
if (prefix!=NULL) {
strcpy(tptr,prefix);
tptr=tptr+strlen(prefix);
*tptr='\0';
}
subnum=0;
found=FALSE;
while (!found) {
tptr[0]=(subnum / 10)+'0';
tptr[1]=(subnum % 10)+'0';
tptr[2]='.';
tmpind=0;
while (type[tmpind]>'\0') { /* if splits[tn], type!=NULL... */
tptr[3+tmpind]=type[tmpind];
tmpind++;
}
tptr[3+tmpind]='\0';
fc=0;
rin.r[0]=9;
rin.r[1]=(int)scratch;
rin.r[2]=(int)buf;
rin.r[3]=MAXENTS;
rin.r[4]=0;
rin.r[5]=BUFSIZE;
rin.r[6]=(int)"*";
while ((rin.r[4]!=-1) && (kerr==NULL)) {
kerr=_kernel_swi(OS_GBPB,&rin,&rout);
if (kerr==NULL) {
fc+=rout.r[3];
rin.r[4]=rout.r[4];
}
}
if (fc<dsize) {
found=TRUE;
} else {
subnum++;
}
}
tptr[2]='.';
tptr[3]='\0';
} /* if split */
strcpy(roname,scratch);
tptr=roname+strlen(scratch);
if (type!=NULL) {
strcpy(tptr,type);
tptr+=strlen(type);
*tptr++='.';
}
strcpy(tptr,name);
} else {
/* else roname=directory.name/type */
if (direct!=NULL) {
strcpy(roname,direct);
tptr=roname+strlen(direct);
*tptr++='.';
} else {
tptr=roname;
}
strcpy(tptr,name);
if (type!=NULL) {
tptr=tptr+strlen(name);
*tptr++='/';
strcpy(tptr,type);
}
} /* if type in types list */
} /* type is not null */
} else {
strcpy(roname,direct);
} /* if just a directory */
if (name!=NULL) {
return(TRUE);
} else {
return(FALSE);
}
}
int main(int argc, char **argv) {
char *tarfile=NULL;
char *tfile=NULL;
int tabort=FALSE;
FILE *fhandle;
FILE *ohandle;
FILE *lhandle;
char line[MAXLEN];
char *lc=NULL;
int ln, titem;
HBlock block;
_kernel_oserror *kerr;
_kernel_swi_regs rin, rout;
int filesize;
char filename[MAXLEN];
char linkname[MAXLEN];
int endblock;
char *cptr;
/* Initialise global data */
verbose=FALSE;
list=FALSE;
dsize=-1;
crunch=FALSE;
tc=0;
prefix=NULL;
/* Process arguments */
argv++; /* skip filename */
while (argc>0) {
switch (argv[0][0]) {
case '-':
switch ((argv[0][1] | 0x20)) {
case 'v':
verbose=TRUE;
break;
case 'l':
list=TRUE;
break;
case 'c':
crunch=TRUE;
break;
case 'd':
if (dsize!=-1) {
(void) fprintf(stderr, "ExTARct: Only one -d value, please\n");
tabort=TRUE;
} else {
if (argc>1) {
dsize=atoi(argv[1]);
argv++;
argc--;
} else {
(void) fprintf(stderr, "ExTARct: -d requires parameter (number)\n");
tabort=TRUE;
}
}
break;
case 't':
if (tfile!=NULL) {
(void) fprintf(stderr, "ExTARct: Only one tfile, please\n");
tabort=TRUE;
} else {
if (argc>1) {
tfile=argv[1];
argv++;
argc--;
} else {
(void) fprintf(stderr, "ExTARct: -t requires parameter (filename)\n");
tabort=TRUE;
}
}
break;
case 'p':
if (prefix!=NULL) {
(void) fprintf(stderr, "ExTARct: Only one -p value, please\n");
tabort=TRUE;
} else {
if (argc>1) {
prefix=argv[1];
argv++;
argc--;
} else {
(void) fprintf(stderr, "ExTARct: -p requires parameter (prefix string)\n");
tabort=TRUE;
}
}
break;
default:
(void) fprintf(stderr, "ExTARct: Bad option %c\n", argv[0][1]);
tabort=TRUE;
}
break;
default:
/* Must be filename */
if (argv[0]!=NULL) tarfile=argv[0];
}
argv++;
argc--;
}
if (dsize==-1) dsize=60;
/* Read configuration file */
if (tfile != NULL) {
if ((fhandle=fopen(tfile,"r"))==NULL) {
(void) fprintf(stderr, "ExTARct: Open failed for T file\n");
tabort=TRUE;
}
while (!feof(fhandle) && !tabort) {
if (fgets(line, MAXLEN, fhandle)==NULL) {
if (!feof(fhandle)) {
(void) fprintf(stderr, "ExTARct: Read failed for T file\n");
tabort=TRUE;
}
} else {
lc=line;
while (*lc==' ' || *lc=='\t') lc++;
if (*lc!='#') {
splits[tc]=FALSE;
ln=0;
while (lc[ln]>' ') ln++;
types[tc]=malloc(ln+1);
strncpy(types[tc],lc,ln);
types[tc][ln]='\0';
while (lc[ln]==' ' || lc[ln]=='\t') ln++;
if (lc[ln]>' ') {
switch (lc[ln]) {
case '*':
splits[tc]=TRUE;
break;
default:
(void) fprintf(stderr, "ExTARct: Bad switch in Tfile (%c)\n", lc[ln]);
tabort=TRUE;
}
}
tc++;
}
}
}
fclose(fhandle);
}
/* tarfile MUST be present :-) Don't really want (can't be bothered...) to use stdin by default */
if (tarfile==NULL) {
(void) fprintf(stderr, "ExTARct: Tar file is mandatory\n");
tabort=TRUE;
}
/* Display accumulated settings */
if (verbose) {
(void) fprintf(stdout, "ExTARct v0.01 (C) 1995 Kevin F. Quinn\n Email kevq@banana.demon.co.uk\n\n");
(void) fprintf(stdout, "Settings:\n");
(void) fprintf(stdout, " Directory limit %d\n", dsize);
(void) fprintf(stdout, " Verbose ON\n");
(void) fprintf(stdout, " Prefix: %s\n",(prefix==NULL)?"(none)":prefix);
(void) fprintf(stdout, " Types:%s\n",(tc==0)?" (none)":"");
titem=0;
while (titem<tc) {
(void) fprintf(stdout, " %s%s\n",types[titem],splits[titem]?" (split)":"");
titem++;
}
}
/* Report error and exit, if anything untoward was detected when reading options */
if (tabort) {
(void) fprintf(stderr, "Syntax:\n ExTARct [options] <tarfile>");
(void) fprintf(stderr, " where options are as follows:\n");
(void) fprintf(stderr, " -v The ubiquitous verbose option\n");
(void) fprintf(stderr, " -d <number> The number of files to one directory (default 60)\n");
(void) fprintf(stderr, " -t <filename> Filename listing types to swap, tagged for directory split\n");
(void) fprintf(stderr, " (default is no swaps, no directory splits)\n");
(void) fprintf(stderr, " -p <prefix> Prefix for split subdirectories (default none)\n");
(void) fprintf(stderr, " -l List operations (i.e. filename mappings) only\n");
return(EXIT_FAILURE);
}
/* Now on to the real business of extracting the archive */
if ((fhandle=fopen(tarfile,"rb"))==NULL) {
(void) fprintf(stderr, "Unable to open file %s\n", tarfile);
return(EXIT_FAILURE);
} /* if fhandle == NULL */
endblock=FALSE;
while (!feof(fhandle) && !tabort && !endblock) {
if (fread(&block, sizeof(HBlock), 1, fhandle)!=1) {
(void) fprintf(stderr, "Bad block; must be 512 bytes\n");
tabort=TRUE;
} else {
if (block.dbuf.name[0]=='\0') endblock=TRUE;
if (!endblock) {
if (!translatename(block.dbuf.name, filename)) { /* directory only*/
if (list) {
(void) fprintf(stdout, "Directory %s\n",filename);
} else {
if (verbose) (void) fprintf(stdout, "Directory %s\n",filename);
strcpy(line,filename); /* Need to tag '.' on end of directory */
cptr=line+strlen(filename);
*cptr++='.';
*cptr='\0';
if (!makedir(line)) {
(void) fprintf(stdout, "Unable to create directory %s (%s)\n",
filename,block.dbuf.name);
}
}
} else {
if (block.dbuf.linkflag=='1' || block.dbuf.linkflag=='2') {
/* Entry is a link.
* Create a LinkFS image. These are files of type FC0, which simply
* contain the canonical name of the file, followed by a '.' and a '\0'.
* Only symbolic links are available, so I generate a symbolic link for hard links.
*/
translatename(block.dbuf.linkname, linkname);
if (list) {
(void) fprintf(stdout, "Linking %s to %s (original names %s and %s)\n",
filename,linkname,block.dbuf.name,block.dbuf.linkname);
} else {
if (verbose)
(void) fprintf(stdout, "Linking %s to %s (original names %s and %s)\n",
filename,linkname,block.dbuf.name,block.dbuf.linkname);
if ((lhandle=fopen(linkname,"wb"))==NULL) {
/* Try to create directory (if relevant) - note this is necessary for the "sub"ed files */
if (!makedir(linkname)) {
(void) fprintf(stderr, "Unable to create directory for link %s (to %s)\n",linkname,filename);
tabort=TRUE;
} else {
if ((lhandle=fopen(linkname,"wb"))==NULL) {
(void) fprintf(stderr, "Unable to create link %s (to %s)\n",linkname,filename);
tabort=TRUE;
}
}
} /* ohandle==NULL*/
if (!tabort) {
/* Write link */
fputs(filename,lhandle);
fputc('.',lhandle);
fputc('\0',lhandle);
fclose(lhandle);
/* Settype to 0xFC0 */
rin.r[0]=18;
rin.r[1]=(int)linkname;
rin.r[2]=0xFC0;
kerr=_kernel_swi(OS_File, &rin, &rout);
if (kerr!=NULL)
(void) fprintf(stderr, "Unable to settype %s to 0xFC0\n",linkname);
} /* if lhandle==NULL */
} /* if list */
} else {
filesize=otoi(block.dbuf.size);
if (list) {
(void) fprintf(stdout, "Creating %s (original name %s)\n",
filename,block.dbuf.name);
while (filesize>0 && !tabort) {
if (fread(&block, sizeof(HBlock), 1, fhandle)!=1) {
(void) fprintf(stderr, "Read failed\n");
tabort=TRUE;
} /* if fread(block) */
filesize=(filesize>TBLOCK)?(filesize-TBLOCK):0;
} /* while filesize>0 */
} else {
if (verbose)
(void) fprintf(stdout, "Creating %s (original name %s)\n",
filename,block.dbuf.name);
if ((ohandle=fopen(filename,"wb"))==NULL) {
/* Try to create directory (if relevant) - note this is necessary for the "sub"ed files */
if (!makedir(filename)) {
(void) fprintf(stderr, "Couldn't create directory for %s\n", filename);
tabort=TRUE;
} else {
if ((ohandle=fopen(filename,"wb"))==NULL) {
(void) fprintf(stderr, "Couldn't create %s\n", filename);
tabort=TRUE;
}
}
} /* ohandle==NULL*/
if (!tabort) {
while (filesize>0 && !tabort) {
if (fread(&block, sizeof(HBlock), 1, fhandle)!=1) {
(void) fprintf(stderr, "Read failed\n");
tabort=TRUE;
} else {
if (fwrite(&block, (filesize>TBLOCK)?TBLOCK:filesize, 1, ohandle)!=1) {
(void) fprintf(stderr, "Write failed\n");
tabort=TRUE;
} /* if fwrite(block) */
filesize=(filesize>TBLOCK)?(filesize-TBLOCK):0;
} /* if fread(block) */
} /* while filesize>0 */
fclose(ohandle);
} /* !tabort */
} /* if list */
} /* if not a link */
} /* if directory */
} /* if block.dbuf.name[0]!='\0' */
} /* if fread(block) */
} /* while (!end of file) */
if (tabort) {
(void) fprintf(stderr, "Processing aborted\n");
return(EXIT_FAILURE);
} /* tabort */
return(EXIT_SUCCESS);
}